package com.ncui.wisdomLearn.biz.user.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ncui.wisdomLearn.api.dto.LoginDTO;
import com.ncui.wisdomLearn.api.dto.UserDTO;
import com.ncui.wisdomLearn.api.entity.User;
import com.ncui.wisdomLearn.api.vo.LoginVO;
import com.ncui.wisdomLearn.api.vo.RoleVO;
import com.ncui.wisdomLearn.api.vo.UserVO;
import com.ncui.wisdomLearn.biz.user.service.*;
import com.ncui.wisdomLearn.common.common.R;
import com.ncui.wisdomLearn.common.exception.BizException;
import com.ncui.wisdomLearn.common.utils.RedisLoginUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.List;
import java.util.Objects;

import static cn.hutool.core.text.CharSequenceUtil.isBlank;

/**
 * 登录服务实现类
 *
 * @author lvlele
 * @since 2025-01-07 15:12:13
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LoginServiceImpl implements LoginService {

    private final UserService userService;
    private final RoleService roleService;
    private final UserRoleService userRoleService;
    private final LoginLogService loginLogService;
    private final RedisLoginUtils redisLoginUtils;

    @Override
    public R<LoginVO> login(LoginDTO loginDTO) {
        // 参数校验
        if ((isBlank(loginDTO.getPhone()) && isBlank(loginDTO.getEmail()))) {
            throw new BizException("登录账号不能为空");
        }

        // 查询用户
        User user = userService.getOne(Wrappers.lambdaQuery(User.class).eq(User::getEmail, loginDTO.getEmail()).last("limit 1"));
        if (user == null) {
            throw new BizException("用户不存在");
        }

        // 检查登录失败次数
        String loginKey = loginDTO.getEmail() != null ? loginDTO.getEmail() : loginDTO.getPhone();
        if (redisLoginUtils.isExceedMaxFailCount(loginKey)) {
            throw new BizException("登录失败次数过多，请24小时后再试");
        }

        // 校验密码
        String encryptedPassword = SecureUtil.md5(loginDTO.getPassword() + user.getSalt());
        if (!encryptedPassword.equals(user.getPassword())) {
            // 增加失败次数
            long failCount = redisLoginUtils.incrementLoginFailCount(loginKey);
            throw new BizException("密码错误，今日还剩" + (10 - failCount) + "次机会");
        }

        // 校验用户状态
        if (user.getStatus() != 0) {
            throw new BizException("用户已被禁用");
        }

        // 检查是否已经登录
        if (StpUtil.isLogin() && user.getUserId().equals(StpUtil.getLoginIdAsLong())) {
            throw new BizException("您已登录，请勿重复登录");
        }

        // 清除登录失败次数
        redisLoginUtils.clearLoginFailCount(loginKey);

        // 执行登录
        StpUtil.login(user.getUserId());
        String token = StpUtil.getTokenValue();

        // 记录登录日志
        String loginIp = getRequestIp();
        loginLogService.recordLogin(user.getUserId(), loginIp);

        // 获取用户角色信息
        List<Long> roleIds = userRoleService.getRoleIdsByUserId(user.getUserId());
        List<RoleVO> roles = roleService.listRolesByIds(roleIds);

        // 构建返回对象
        LoginVO loginVO = BeanUtil.copyProperties(user, LoginVO.class, "phone");
        loginVO.setRoles(roles);
        loginVO.setToken(token);

        return R.ok(loginVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Void> register(LoginDTO loginDTO) {
        // 参数校验
        if ((isBlank(loginDTO.getPhone()) && isBlank(loginDTO.getEmail()))) {
            throw new BizException("注册账号不能为空");
        }

        // 转换为UserDTO
        UserDTO userDTO = new UserDTO();
        userDTO.setUsername(loginDTO.getUsername());
        userDTO.setPhone(loginDTO.getPhone());
        userDTO.setEmail(loginDTO.getEmail());
        userDTO.setPassword(loginDTO.getPassword());

        // 创建用户
        if (Boolean.FALSE.equals(userService.createUser(userDTO))) {
            throw new BizException("注册失败");
        }

        return R.ok("注册成功");
    }

    @Override
    public R<Void> logout() {
        if (StpUtil.isLogin()) {
            // 记录登出日志
            loginLogService.recordLogout(StpUtil.getLoginIdAsLong());
            // 注销登录
            StpUtil.logout();
        }
        return R.ok("退出成功");
    }

    @Override
    public R<Boolean> getLoginStatus() {
        // 检查是否已登录
        if (!StpUtil.isLogin()) {
            return R.ok(true);
        }

        // 获取当前登录用户ID
        Long userId = StpUtil.getLoginIdAsLong();

        // 获取用户信息
        UserVO userVO = userService.getUserById(userId);
        if (userVO == null) {
            StpUtil.logout();
            return R.ok(false);
        }

        return R.ok(true);
    }

    @Override
    public R<LoginVO> getTokenInfo() {
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();

        // 获取用户信息
        Long userId = Long.parseLong(tokenInfo.getLoginId().toString());
        UserVO userVO = userService.getUserById(userId);
        if (userVO == null) {
            StpUtil.logout();
            throw new BizException("用户不存在");
        }

        // 获取用户角色信息
        List<Long> roleIds = userRoleService.getRoleIdsByUserId(userId);
        List<RoleVO> roles = roleService.listRolesByIds(roleIds);

        // 构建返回对象
        LoginVO loginVO = BeanUtil.copyProperties(userVO, LoginVO.class, "phone", "email");
        loginVO.setRoles(roles);
        loginVO.setToken(tokenInfo.getTokenValue());

        return R.ok(loginVO);
    }

    /**
     * 获取请求IP地址
     *
     * @return IP地址
     */
    private String getRequestIp() {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
} 